package gm.server;

import java.util.ArrayList;

/**
 * Motor.
 *
 * @author imruf84
 */
public class Motor {

    /**
     * Aktuális lépések száma.
     */
    private double currentSteps = 0;
    /**
     * Lépések száma per fordulat.
     */
    private final double spr;
    /**
     * Név.
     */
    private final String name;
    /**
     * Azonosító.
     */
    private final String ID;
    /**
     * Fordulat per perc.
     */
    private final double rpm;
    /**
     * Elfordulás eseményeinek a tárolója.
     */
    private final ArrayList<MotorStepEvent> onStepEvents = new ArrayList<>();
    /**
     * Motor parncs letiltva.
     */
    private boolean motorActionDisabled = false;

    /**
     * Konstruktor.
     *
     * @param ID azonosító
     * @param name név
     * @param rpm fordulat per perc
     * @param spr lépések száma fordulatonként
     */
    public Motor(String ID, String name, double rpm, double spr) {
        this.ID = ID;
        this.name = name;
        this.rpm = rpm;
        this.spr = spr;
    }

    /**
     * Azonosító lekérdezése.
     *
     * @return azonosító
     */
    public String getID() {
        return ID;
    }

    /**
     * Név lekérdezése.
     *
     * @return név
     */
    public String getName() {
        return name;
    }

    /**
     * Sebesség lekérdezése.
     *
     * @return sebesség
     */
    public double getRotationsPerMinute() {
        return rpm;
    }

    /**
     * Lépések száma per fordulat lekérdezése.
     *
     * @return lépések száma per fordulat
     */
    public double getStepsPerRotation() {
        return spr;
    }

    /**
     * Motor parancsok engedélyezése.
     */
    public void enableMotorAction() {
        motorActionDisabled = false;
    }

    /**
     * Motor parancsok letiltása.
     */
    public void disableMotorAction() {
        motorActionDisabled = true;
    }

    /**
     * Tényleges motor esemény.
     *
     * @param steps lépések száma
     */
    protected void doMotorAction(final double steps) {

        if (motorActionDisabled) {
            return;
        }

        double asteps = Math.abs(steps);
        double speed = 100;
        double d = (steps / asteps);

        while (0 < asteps) {
            setCurrentSteps(getCurrentStepsAbsolute() + (Math.min(speed, asteps) * d), true);
            asteps -= speed;
        }

        // HACK: ez azért kell, mert a metódus végén szintén hozzáadom a megtett lépést, így az duplázódna.
        setCurrentSteps(getCurrentStepsAbsolute() - steps, false);
    }

    /**
     * Adott lépésszám megtétele.
     *
     * @param steps lépések száma
     * @return előző lépések száma
     */
    public double doSteps(final double steps) {
        double s = getCurrentStepsAbsolute();

        doMotorAction(steps);
        setCurrentSteps(currentSteps + steps, true);

        return s;
    }

    /**
     * Egy lépés megtétele.
     *
     * @return előző lépések száma
     */
    public double doOneStep() {
        return doSteps(1);
    }

    /**
     * Adott számú fordulat megtétele.
     *
     * @param turns körbefordulások száma
     * @return előző lépések száma
     */
    public double doRotations(final double turns) {
        return doSteps(turns * getStepsPerRotation());
    }

    /**
     * Egy fordulat megtétele.
     *
     * @return előző lépések száma
     */
    public double doOneRotation() {
        return doRotations(1);
    }

    /**
     * Aktuális lépések számának a lekérdezése.
     *
     * @return aktuális lépések száma
     */
    public double getCurrentStepsAbsolute() {
        return currentSteps;
    }

    /**
     * Aktuális lépések számának a megadása.
     *
     * @param steps lépések száma
     * @param fireEvents igaz esetén a változás eseménye is lefut
     */
    public void setCurrentSteps(double steps, boolean fireEvents) {

        currentSteps = steps;

        if (fireEvents) {
            fireOnStepEvents();
        }
    }

    /**
     * Aktuális lépések számának a megadása az események futtatása nélkül.
     *
     * @param steps lépések száma
     */
    public void setCurrentStepsSilent(double steps) {
        setCurrentSteps(steps, false);
    }

    /**
     * Elfordulás esemény hozzáadása.
     *
     * @param event esemény
     */
    public void addOnStepEvent(final MotorStepEvent event) {
        onStepEvents.add(event);
    }

    /**
     * Elfordulás események meghívása.
     */
    private void fireOnStepEvents() {
        for (MotorStepEvent e : onStepEvents) {
            e.onStep(this);
        }
    }

    /**
     * Lépések átváltása fordulattá.
     *
     * @param steps lépések száma
     * @return fordulatok száma
     */
    public double stepsToRotation(double steps) {
        return steps / getStepsPerRotation();
    }

}
